#pragma once
#include <limits.h>
#include <list>
#include <istream>
#include <iostream>
#include <string>
#include <vector>
#include <boost/array.hpp>
#include <boost/intrusive/list.hpp>
#include "instruction.hpp"
#include "keytree.hpp"

typedef std::vector<int> IntegerArray;

struct Program
{
	Program();
	Program(std::istream & stream);
	Program(Program const& other);

	struct ExecutionResult
	{
		enum ErrorCode {
			  NoError = 0
			, MaxExecution
			, NoInstructions
		};

		ExecutionResult()
			: ret_code_(-1)
			, cost_(0)
			, error_(NoError)
		{}

		bool operator==(ExecutionResult const& er) const {
			return cost() == er.cost() && return_code() == er.return_code() && error() == er.error();
		}

		unsigned cost() const { return cost_; }
		int return_code() const { return  ret_code_; }
		ErrorCode error() const { return error_; }

	private:
		friend struct Program;

		int ret_code_;
		unsigned cost_;
		IntegerArray bytes_;
		ErrorCode error_;
	};

	void push_back(Instruction const& inst);

	static const int ten_million = 10 * 1000 * 1000;
	ExecutionResult execute(std::vector<IntegerArray> const& input = std::vector<IntegerArray>(), unsigned max_cost = ten_million);

	// TODO: add async_execute
	
	std::string disassemble() const;
	void write(std::ostream & stream) const;
private:
	struct ProgramInstruction
		: public boost::intrusive::list_base_hook<>
		, public Instruction
	{
		ProgramInstruction(int id, int op, int argA, int argB, int ret);
	};

	int create_before(int before, int opcode);
	int create_after(int after, int opcode);
	
	void set(ChangeType, int identifier, int value);

	KeyTree<int, ProgramInstruction> instructions_;

	typedef boost::intrusive::list<ProgramInstruction, boost::intrusive::constant_time_size<false> > OrderType;

	OrderType instruction_order_;
};

inline std::ostream & operator<<(std::ostream & str, Program::ExecutionResult const& er) {
	str << "[Return: " << er.return_code() << ", cost:" << er.cost() << ", Error: " << er.error() << "]";
	return str;
}



